package arrowandsphere;

import java.io.*;
import java.util.*;

public class Session implements Serializable {
    //Constants
    public static final int BOARD_WIDTH = 9;
    public static final int BOARD_HEIGHT = 9;
    public static final int NUMBER_OF_PIECES = 2;
    public static final int SPHERE_INITIAL_X = 4;
    public static final int SPHERE_INITIAL_Y = 4;
    
    public static Vector<Vector<Integer>> pieceBuffer = new Vector<Vector<Integer>>();
    public static final int MAX_PIECES = 10;

    //Board
    private int [][] board;
    //Corners
    private int [] cornerX;
    private int [] cornerY;
    //Players
	private List<String> players;
    private int maxPlayers;
    //Player pieces
    private int [][] pieces;
    //Sphere
    private int sphereX;
    private int sphereY;
    //Turn
    private int turn;
	
	public Session(int maxPlayers) {
        board = new int [BOARD_WIDTH][BOARD_HEIGHT];
        for (int i = 0; i < BOARD_WIDTH; ++i) {
            for (int j = 0; j < BOARD_HEIGHT; ++j) {
                board[i][j] = CellConstants.EMPTY;
            }
        }
        board[SPHERE_INITIAL_Y][SPHERE_INITIAL_X] = CellConstants.SPHERE;
        sphereX = SPHERE_INITIAL_X;
        sphereY = SPHERE_INITIAL_Y;
        
        this.maxPlayers = maxPlayers;
        players = new Vector<String>();
		
        pieces = new int [maxPlayers][NUMBER_OF_PIECES];
        for (int i = 0; i < maxPlayers; ++i) {
            for (int j = 0; j < NUMBER_OF_PIECES; ++j) {
                pieces[i][j] = generateRandomPiece();
            }
        }
        turn = 0;
        
        cornerX = new int [maxPlayers];
        cornerY = new int [maxPlayers];
        //Player 1 - Top left corner
        cornerX[0] = 0;
        cornerY[0] = 0;
        //Player 2 - Bottom right corner
        cornerX[1] = BOARD_WIDTH - 1;
        cornerY[1] = BOARD_HEIGHT - 1;
	}
    
    public void addPlayer(String player) {
        players.add(player);
    }
    
    public void discardPiece(int playerIdx, int pieceIdx) {
        pieces[playerIdx][pieceIdx] = generateRandomPiece();
        turn = (turn + 1) % maxPlayers;
    }
    
    public int [][] getBoard() {
        return board;
    }
    
    public int getPlayerFromCorner(int x, int y) {
        if ((x == 0) && (y == 0)) {
            return 1;
        }
        else if ((x == BOARD_WIDTH - 1) && (y == BOARD_HEIGHT - 1)) {
            return 2;
        }
        return -1;
    }
    
    public int getPlayerIndex(String playerName) {
        return players.indexOf(playerName);
    }
    
    public int [] getPlayerPieces(int playerIdx) {
        return pieces[playerIdx];
    }
    
    private static int generateRandomPiece() {
        Random r = new Random();
        int i = r.nextInt(101);
        //Probability of a directional piece: 80%
        if (i >= 0 && i <= 80) {
            return r.nextInt(8) + 1;
        }
        //Probability of a joker: 10%
        else if (i > 80 && i <= 90) {
            return CellConstants.JOKER;
        }
        //Probability of any other piece: 10%
        return r.nextInt(2) + 10;
    }
    
        
    public int getVictoriousPlayer() {
        for (int i = 0; i < cornerX.length; ++i) {
            if ((sphereX == cornerX[i]) && (sphereY == cornerY[i])) {
                return i + 1;
            }
        }
        return 0;
    }
    
    public boolean isTurnOf(int playerIdx) {
        return (turn == playerIdx);
    }
    
    public void moveSphere(int piece) throws Exception {
        int newSphereX = -1;
        int newSphereY = -1;
        //Determine the new position for the sphere
        if (piece == CellConstants.NORTHWEST) {
            newSphereX = sphereX - 1;
            newSphereY = sphereY - 1;
        }
        else if (piece == CellConstants.NORTH) {
            newSphereX = sphereX;
            newSphereY = sphereY - 1;
        }
        else if (piece == CellConstants.NORTHEAST) {
            newSphereX = sphereX + 1;
            newSphereY = sphereY - 1;
        }
        else if (piece == CellConstants.WEST) {
            newSphereX = sphereX - 1;
            newSphereY = sphereY;
        }
        else if (piece == CellConstants.EAST) {
            newSphereX = sphereX + 1;
            newSphereY = sphereY;
        }
        else if (piece == CellConstants.SOUTHWEST) {
            newSphereX = sphereX - 1;
            newSphereY = sphereY + 1;
        }
        else if (piece == CellConstants.SOUTH) {
            newSphereX = sphereX;
            newSphereY = sphereY + 1;
        }
        else if (piece == CellConstants.SOUTHEAST) {
            newSphereX = sphereX + 1;
            newSphereY = sphereY + 1;
        }
        else {
            throw new IllegalArgumentException("Illegal piece.");
        }
        //Is the new position within the board?
        if ((newSphereX < 0) || (newSphereX >= BOARD_WIDTH) || (newSphereY < 0) || (newSphereY >= BOARD_HEIGHT)) {
            throw new Exception("Illegal move: the sphere would be placed outside of the board.");
        }
        //Is the target cell empty?
        if (board[newSphereY][newSphereX] != CellConstants.EMPTY) {
            throw new Exception("Illegal move: that cell already contains a piece.");
        }
        //All clear!
        Vector<Integer> pos = new Vector<Integer>();
    	pos.add(sphereX);
    	pos.add(sphereY);
        pieceBuffer.add(pos);
        if(pieceBuffer.size() > MAX_PIECES){
        	board[pieceBuffer.get(0).get(1)][pieceBuffer.get(0).get(0)] = CellConstants.EMPTY;
        	pieceBuffer.remove(0);
        }
        
        sphereX = newSphereX;
        sphereY = newSphereY;
        board[sphereY][sphereX] = CellConstants.SPHERE;
    }
    
    public void moveSphereTwoCells(int piece) throws Exception {
        int newSphereX = -1;
        int newSphereY = -1;
        //Determine the new position for the sphere
        if (piece == CellConstants.NORTHWEST) {
            newSphereX = sphereX - 2;
            newSphereY = sphereY - 2;
        }
        else if (piece == CellConstants.NORTH) {
            newSphereX = sphereX;
            newSphereY = sphereY - 2;
        }
        else if (piece == CellConstants.NORTHEAST) {
            newSphereX = sphereX + 2;
            newSphereY = sphereY - 2;
        }
        else if (piece == CellConstants.WEST) {
            newSphereX = sphereX - 2;
            newSphereY = sphereY;
        }
        else if (piece == CellConstants.EAST) {
            newSphereX = sphereX + 2;
            newSphereY = sphereY;
        }
        else if (piece == CellConstants.SOUTHWEST) {
            newSphereX = sphereX - 2;
            newSphereY = sphereY + 2;
        }
        else if (piece == CellConstants.SOUTH) {
            newSphereX = sphereX;
            newSphereY = sphereY + 2;
        }
        else if (piece == CellConstants.SOUTHEAST) {
            newSphereX = sphereX + 2;
            newSphereY = sphereY + 2;
        }
        else {
            throw new IllegalArgumentException("Illegal piece.");
        }
        //Is the new position within the board?
        if ((newSphereX < 0) || (newSphereX >= BOARD_WIDTH) || (newSphereY < 0) || (newSphereY >= BOARD_HEIGHT)) {
            throw new Exception("Illegal move: the sphere would be placed outside of the board.");
        }
        //Is the target cell empty?
        if (board[newSphereY][newSphereX] != CellConstants.EMPTY) {
            throw new Exception("Illegal move: that cell already contains a piece.");
        }
        //All clear!
        Vector<Integer> pos = new Vector<Integer>();
    	pos.add(sphereX);
    	pos.add(sphereY);
        pieceBuffer.add(pos);
        if(pieceBuffer.size() > MAX_PIECES){
        	board[pieceBuffer.get(0).get(1)][pieceBuffer.get(0).get(0)] = CellConstants.EMPTY;
        	pieceBuffer.remove(0);
        }
        
        sphereX = newSphereX;
        sphereY = newSphereY;
        board[sphereY][sphereX] = CellConstants.SPHERE;
    }
    
    public void moveSphereToRandomCell() {
        int newSphereX = -1;
        int newSphereY = -1;
        //Determine the new position for the sphere
        Random r = new Random();
        while (true) {
            newSphereX = r.nextInt(BOARD_WIDTH);
            newSphereY = r.nextInt(BOARD_HEIGHT);
            if (board[newSphereY][newSphereX] == CellConstants.EMPTY) break;
        }
        //All clear!
        Vector<Integer> pos = new Vector<Integer>();
    	pos.add(sphereX);
    	pos.add(sphereY);
        pieceBuffer.add(pos);
        if(pieceBuffer.size() > MAX_PIECES){
        	board[pieceBuffer.get(0).get(1)][pieceBuffer.get(0).get(0)] = CellConstants.EMPTY;
        	pieceBuffer.remove(0);
        }
        
        sphereX = newSphereX;
        sphereY = newSphereY;
        board[sphereY][sphereX] = CellConstants.SPHERE;
    }
    
    public void playPiece(int playerIdx, int pieceIdx, String jokerChoice) throws Exception {
        int piece = pieces[playerIdx][pieceIdx];
        int otherPiece = pieces[playerIdx][(pieceIdx + 1) % NUMBER_OF_PIECES];
        //Is it a joker? If so, turn the joker into a directional piece.
        if (piece == CellConstants.JOKER) {
            if (jokerChoice.equals("northwest")) {
                piece = CellConstants.NORTHWEST;
            }
            else if (jokerChoice.equals("north")) {
                piece = CellConstants.NORTH;
            }
            else if (jokerChoice.equals("northeast")) {
                piece = CellConstants.NORTHEAST;
            }
            else if (jokerChoice.equals("west")) {
                piece = CellConstants.WEST;
            }
            else if (jokerChoice.equals("east")) {
                piece = CellConstants.EAST;
            }
            else if (jokerChoice.equals("southwest")) {
                piece = CellConstants.SOUTHWEST;
            }
            else if (jokerChoice.equals("south")) {
                piece = CellConstants.SOUTH;
            }
            else if (jokerChoice.equals("southeast")) {
                piece = CellConstants.SOUTHEAST;
            }
            int oldX = sphereX;
            int oldY = sphereY;
            moveSphere(piece);
            board[oldY][oldX] = CellConstants.JOKER;
            //Discard the piece we played and fetch another
            pieces[playerIdx][pieceIdx] = generateRandomPiece();
        }
        //Is it a double? If so, try to use the other piece for double movement
        else if (piece == CellConstants.DOUBLE) {
            int oldX = sphereX;
            int oldY = sphereY;
            moveSphereTwoCells(otherPiece);
            board[oldY][oldX] = CellConstants.DOUBLE;
            //Discard the played pieces and feth others in their place
            pieces[playerIdx][pieceIdx] = generateRandomPiece();
            pieces[playerIdx][(pieceIdx + 1) % NUMBER_OF_PIECES] = generateRandomPiece();
        }
        //Is it a warp? If so, move the sphere to a random, empty cell
        else if (piece == CellConstants.WARP) {
            int oldX = sphereX;
            int oldY = sphereY;
            moveSphereToRandomCell();
            board[oldY][oldX] = CellConstants.WARP;
            //Discard the piece we played and fetch another
            pieces[playerIdx][pieceIdx] = generateRandomPiece();
        }
        //It's a regular piece
        else {
            int oldX = sphereX;
            int oldY = sphereY;
            moveSphere(piece);
            board[oldY][oldX] = piece;
            pieces[playerIdx][pieceIdx] = generateRandomPiece();
        }
        turn = (turn + 1) % maxPlayers;
    }
}